package ru.ipo.dces.plugins.admin.components;

import ru.ipo.dces.client.Controller;
import ru.ipo.dces.clientservercommunication.ContestDescription;
import ru.ipo.dces.clientservercommunication.GetUsersResponse;
import ru.ipo.dces.clientservercommunication.UserDescription;
import ru.ipo.dces.server.ServerFailReason;
import ru.ipo.dces.utils.ResponseHandler;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.event.*;
import java.util.HashSet;

public class ContestUsersTable extends JTable {

    private ContestDescription contest;
    private static UserDescription[] userDescriptions;
    private final ContestUsersAbstractTableModel model;
    private HashSet<MouseListener> clickOnTableListeners = new HashSet<MouseListener>();

    public static final TableCellRenderer loginsRenderer = new TableCellRenderer() {
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                       boolean hasFocus, int row, int column) {
            JLabel label = new JLabel(value.toString());
            if (userDescriptions[row].userType == UserDescription.UserType.ContestAdmin)
                label.setFont(label.getFont().deriveFont(Font.BOLD));
            label.setOpaque(true);
            label.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
            label.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
            return label;
        }
    };

    public static final TableCellRenderer idRenderer = new TableCellRenderer() {
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                       boolean hasFocus, int row, int column) {
            JLabel label = new JLabel(value.toString());
            label.setHorizontalTextPosition(JLabel.LEFT);
            label.setHorizontalAlignment(JLabel.LEFT);
            label.setOpaque(true);
            label.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
            label.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
            return label;
        }
    };

    public ContestUsersTable() {
        super();
        model = new ContestUsersAbstractTableModel();
        setModel(model);
    }

    public void setContest(ContestDescription contest) {
        this.contest = contest;
        model.fireTableStructureChanged();
        addListeners();
        getTableHeader().setReorderingAllowed(false);
    }

    public void refreshContest() {
        if (contest == null)
            throw new IllegalStateException("Contest is not already yet");
        setContest(contest);
    }

    private void addListeners() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {

                setColumnSelectionAllowed(false);
                for (MouseListener listener : clickOnTableListeners)
                    listener.mouseClicked(new MouseEvent(ContestUsersTable.this, 0, 0, 0, 0, 0, 1, false));
            }
        });

        getDefaultEditor(String.class).addCellEditorListener(new CellEditorListener() {
            @Override
            public void editingStopped(ChangeEvent changeEvent) {
//                System.out.println(changeEvent.getSource());
            }

            @Override
            public void editingCanceled(ChangeEvent changeEvent) {
            }
        });

        getTableHeader().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                // The index of the column whose header was clicked
                int vColIndex = getColumnModel().getColumnIndexAtX(e.getX());
//                int mColIndex = convertColumnIndexToModel(vColIndex); // ReorderingAllowed=false

                // Return if not clicked on any column header
                if (vColIndex == -1) {
                    return;
                }

                Rectangle headerRect = getTableHeader().getHeaderRect(vColIndex);
                if (vColIndex == 0)
                    headerRect.width -= 3;    // Hard-coded constant
                else
                    headerRect.grow(-3, 0);   // Hard-coded constant
                if (!headerRect.contains(e.getX(), e.getY()))
                    return;


                System.out.println("Clicked on " + getModel().getColumnName(vColIndex));
            }
        });
    }

    private void setIdColumnWidth() {
        int maxID = 0;
        for (int i = 0; i < getModel().getRowCount(); i++) {
            int userID = (Integer) getModel().getValueAt(i, 0);
            if (userID > maxID)
                maxID = userID;
        }
        JTableHeader th = getTableHeader();
        int prefWidth = Math.round((float) th.getFontMetrics(th.getFont()).getStringBounds(th.getTable().getColumnName(0),
                th.getGraphics()).getWidth());
        getColumnModel().getColumn(0).setMaxWidth(prefWidth + 20);
    }

    public void addRegistrationListener(MouseListener l) {
        clickOnTableListeners.add(l);
    }

    public void removeRegistrationListener(MouseListener l) {
        clickOnTableListeners.remove(l);
    }

    public class ContestUsersAbstractTableModel extends AbstractTableModel {

        public int getRowCount() {
            if (contest == null || userDescriptions == null)
                return 0;
            return userDescriptions.length;
        }

        public int getColumnCount() {
            if (contest == null)
                return 0;
            if (contest.data.length == 1 && contest.data[0] == null)
                return 2;
            return 1 + 1 + contest.data.length; // id + login + fields
        }

        public Object getValueAt(int row, int column) {
            Object result;
            if (column == 0)
                result = userDescriptions[row].userID;
            else if (column == 1)
                result = userDescriptions[row].login;
            else if (userDescriptions[row].dataValue.length == 0)
                result = null;
            else
                result = userDescriptions[row].dataValue[column - 2];
            return result;
        }

        @Override
        public String getColumnName(int column) {
            if (column == 0)
                return "id";
            if (column == 1)
                return "login";
            return contest.data[column - 2].data;
        }

        @Override
        public boolean isCellEditable(int row, int column) {
            return false; //TODO let user edit lines
            //return column != 0 && column != 1;
        }

        @Override
        public Class<?> getColumnClass(int column) {
            if (column == 0)
                return Integer.class;
            return String.class;
        }

        @Override
        public void fireTableStructureChanged() {
            ResponseHandler<GetUsersResponse> handler = new ResponseHandler<GetUsersResponse>() {
                @Override
                public void success(GetUsersResponse response) {
                    userDescriptions = response.users;
                    ContestUsersAbstractTableModel.super.fireTableStructureChanged();
                    setIdColumnWidth();
                    setAutoCreateRowSorter(true);
                    ContestUsersTable.this.getColumnModel().getColumn(1).setCellRenderer(ContestUsersTable.loginsRenderer);
                    ContestUsersTable.this.getColumnModel().getColumn(0).setCellRenderer(ContestUsersTable.idRenderer);
                }

                @Override
                public void fail(ServerFailReason exception) {
                }
            };
            Controller.getUsers(contest.contestID, handler);
        }
    }
}
